home *** CD-ROM | disk | FTP | other *** search
- /* LogManager.c */
- /*
- * LogManager.c
- * Copyright © 1993 Apple Computer Inc. All rights reserved.
- *
- * These functions manage a logging display for error messages and other text.
- * The log is implemented as a ListManager list that can hold nLogItems. This
- * module is intentionally more-or-less self-contained so it can easily be
- * exported to other applications.
- */
- #include "LogManager.h"
- #include <Packages.h>
- /*
- * There is a 32000 byte maximum for the list, so don't
- * make nLogLines too big.
- */
- #ifndef nDefaultLogLines
- #define nDefaultLogLines 128
- #endif
- #define width(r) ((r).right - (r).left)
- #define height(r) ((r).bottom - (r).top)
- #ifndef TRUE
- #define TRUE 1
- #define FALSE 0
- #endif
- enum {
- kScrollBarWidth = 16,
- kScrollBarOffset = kScrollBarWidth - 1,
- kActiveControl = 0, /* Normal button hilite */
- kDisabledControl = 255 /* Disabled button hilite */
- };
- /*
- * When the horizontal scrollbar is at zero, the list is indented +4 pixels.
- * When the horizontal scrollbar increases, the list indent decreases. This
- * value is "by inspection," but we could extract it from the list record
- * when the list is initially created.
- */
- enum {
- kZeroIndent = 4
- };
- /*
- * This sets a nominal value for the maximum cell width. This might be
- * better provided as a user-settable parameter.
- */
- #define kMaxHorizontalScroll (CharWidth('M') * 255)
-
- /*
- * Cheap 'n dirty pascal string copy routine.
- */
- #ifndef pstrcpy
- #define pstrcpy(dst, src) do { \
- StringPtr _src = (src); \
- BlockMove(_src, dst, _src[0] + 1); \
- } while (0)
- #endif
- /*
- * Cheap 'n dirty pascal string concat.
- */
- #ifndef pstrcat
- #define pstrcat(dst, src) do { \
- StringPtr _dst = (dst); \
- StringPtr _src = (src); \
- short _len; \
- _len = 255 - _dst[0]; \
- if (_len > _src[0]) _len = _src[0]; \
- BlockMove(&_src[1], &_dst[1] + _dst[0], _len); \
- _dst[0] += _len; \
- } while (0)
- #endif
-
- static pascal void ScrollLogAction(
- register ControlHandle theControl,
- short partcode
- );
- static void ScrollLogList(
- ControlHandle theControl
- );
-
- /*
- * This record is stored in the userHandle variable in the list. The values are
- * needed to specify the font, limit the number of error log lines that are stored,
- * and preserve the log area color. We also hide the horizontal scrollbar here
- * because, if we leave it in the ListRecord, the ListManager will decide to
- * permanently un-hilite it.
- */
- typedef struct LogInfoRecord {
- ControlHandle hScroll;
- short logLines;
- short fontNumber;
- short fontSize;
- RGBColor foreColor;
- RGBColor backColor;
- } LogInfoRecord, *LogInfoPtr, **LogInfoHdl;
- #define LIST (**logListHandle)
- #define LOGINFO (**((LogInfoHdl) (LIST.userHandle)))
- #define HSCROLL (LOGINFO.hScroll)
- #define IS_COLOR(port) (((((CGrafPtr) (port))->portVersion) & 0xC000) != 0)
- #define COLOR_LIST (IS_COLOR(LIST.port))
-
- /*
- * This code sequence is copied into the ListProc handle. It is designed so we
- * don't have to flush the instruction and data caches. Note that this must
- * be revised if you compile for a non-68000 environment.
- */
- static const short gDummyLDEF[] = {
- 0x207A, /* movea.l procPtr,a0 */
- 0x0004, /* <offset to procPtr> */
- 0x4ED0, /* jmp a0 */
- 0x0000, 0x0000 /* dc.l <Drawing Proc here> */
- };
-
- static pascal void
- LogListDefProc(
- short listMessage,
- Boolean listSelect,
- Rect *listRect,
- Cell listCell,
- short listDataOffset,
- short listDataLen,
- ListHandle errorLogList
- );
-
- /*
- * Create the data display list.
- */
- ListHandle
- CreateLog(
- const Rect *viewRect,
- Boolean hasGrowBox,
- short listFontNumber,
- short listFontSize,
- short logLines
- )
- {
- OSErr status;
- ListHandle logListHandle;
- Point cellSize;
- Rect dataBounds;
- Rect listRect;
- FontInfo info;
- short listHeight;
- short listFontHeight;
- Handle drawProcHdl;
- LogInfoRecord logInfo;
- Handle logInfoHdl;
- ProcPtr listProcPtr;
- short horizontalMax;
-
- logInfoHdl = NULL;
- drawProcHdl = NULL;
- if (logLines == 0)
- logLines = nDefaultLogLines;
- TextFont(listFontNumber);
- TextSize(listFontSize);
- listRect = *viewRect;
- if (hasGrowBox == FALSE) {
- /*
- * Since there is no growBox to align to, we can adjust the list
- * rectangle so there is an integral number of lines on the list.
- */
- GetFontInfo(&info);
- listFontHeight = info.ascent + info.descent + info.leading;
- listHeight = height(listRect);
- listHeight -= (listHeight % listFontHeight);
- listRect.bottom = listRect.top + listHeight;
- }
- /*
- * Note that we create a one-colum list with both vertical and horizontal
- * scrollbars, then we steal the horizontal scrollbar because we're
- * scrolling within the list cell. Unfortunately, the List Manager only
- * scrolls from one cell (column in the horizontal direction) to another.
- */
- SetPt(&cellSize, 0, 0);
- SetRect(&dataBounds, 0, 0, 1, 0);
- logListHandle = LNew(
- &listRect, /* Viewing area */
- &dataBounds, /* Rows and col's */
- cellSize, /* Element size */
- 0, /* No defProc yet */
- qd.thePort, /* Display window */
- TRUE, /* Draw it */
- hasGrowBox, /* User's choice */
- TRUE, /* Has horizontal scroll */
- TRUE /* Has vertical scroll */
- );
- if (logListHandle == NULL)
- goto failure;
- LIST.selFlags = lOnlyOne;
- LIST.listFlags = lDoVAutoscroll; /* Vertical autoscroll only */
- logInfo.logLines = logLines;
- logInfo.fontNumber = listFontNumber;
- logInfo.fontSize = listFontSize;
- if (COLOR_LIST) {
- GetForeColor(&logInfo.foreColor);
- GetBackColor(&logInfo.backColor);
- }
- status = PtrToHand(&logInfo, &logInfoHdl, sizeof logInfo);
- if (status != noErr)
- goto failure;
- LIST.userHandle = (Handle) logInfoHdl;
- HSCROLL = LIST.hScroll; /* Grab horizontal scroller */
- LIST.hScroll = NULL; /* Remove it from the list */
- SetCRefCon(HSCROLL, (long) logListHandle); /* Link scrollbar to list */
- status = PtrToHand(gDummyLDEF, &drawProcHdl, sizeof gDummyLDEF);
- if (status != noErr)
- goto failure;
- listProcPtr = (ProcPtr) LogListDefProc;
- BlockMove(&listProcPtr, &((short *) *drawProcHdl)[3], sizeof listProcPtr);
- LIST.listDefProc = drawProcHdl;
- horizontalMax = kMaxHorizontalScroll - width((**HSCROLL).contrlRect);
- if (horizontalMax < 0)
- horizontalMax = 0;
- SetCtlMin(HSCROLL, 0);
- SetCtlMax(HSCROLL, horizontalMax);
- SetCtlValue(HSCROLL, 0);
- if (horizontalMax == 0)
- HiliteControl(HSCROLL, kDisabledControl);
- goto success;
- failure:
- if (drawProcHdl != NULL) {
- DisposeHandle((Handle) drawProcHdl);
- LIST.listDefProc = NULL;
- }
- DisposeLog(logListHandle);
- logListHandle = NULL;
- success:
- return (logListHandle);
- }
-
- /*
- * DisposeLog disposes of our private information and then disposes of the list.
- */
- void
- DisposeLog(
- ListHandle logListHandle
- )
- {
- if (logListHandle != NULL) {
- if (LIST.userHandle != NULL) {
- LIST.hScroll = HSCROLL; /* The list manager disposes */
- DisposeHandle(LIST.userHandle);
- LIST.userHandle = NULL;
- }
- LDispose(logListHandle);
- }
- }
-
- /*
- * UpdateLog redraws the list.
- */
- void
- UpdateLog(
- ListHandle logListHandle
- )
- {
- Rect viewRect;
- RGBColor saveForeColor;
- RGBColor saveBackColor;
-
- if (logListHandle != NULL) {
- /*
- * Make sure the list is locked down while we draw. Note that we
- * assume that the application has called UpdateControls, so
- * the horizontal scrollbar is correctly drawn.
- */
- if (COLOR_LIST) {
- GetForeColor(&saveForeColor);
- GetBackColor(&saveBackColor);
- RGBForeColor(&LOGINFO.foreColor);
- RGBBackColor(&LOGINFO.backColor);
- }
- viewRect = LIST.rView;
- /*
- * Include the scrollbars in the frame.
- */
- EraseRect(&viewRect);
- InsetRect(&viewRect, -1, -1);
- viewRect.right += kScrollBarOffset;
- viewRect.bottom += kScrollBarOffset;
- FrameRect(&viewRect);
- LUpdate(LIST.port->visRgn, logListHandle);
- if (COLOR_LIST) {
- RGBForeColor(&saveForeColor);
- RGBBackColor(&saveBackColor);
- }
- }
- }
-
- /*
- * ActivateLog activates (or deactivates) the log. Call it on activate and
- * suspendResume events.
- */
- void
- ActivateLog(
- ListHandle logListHandle,
- Boolean activating
- )
- {
- if (logListHandle != NULL) {
- LActivate(activating, logListHandle);
- HiliteControl(
- HSCROLL,
- (activating) ? kActiveControl : kDisabledControl);
- }
- }
-
- /*
- * MoveLog Repositions the log list area.
- */
- void
- MoveLog(
- ListHandle logListHandle,
- short leftEdge,
- short topEdge
- )
- {
- Rect viewRect;
-
- if (logListHandle != NULL) {
- if (LIST.rView.left != leftEdge || LIST.rView.top != topEdge) {
- viewRect = LIST.rView;
- InsetRect(&viewRect, -1, -1);
- InvalRect(&viewRect);
- OffsetRect(
- &LIST.rView,
- leftEdge - LIST.rView.left,
- topEdge -LIST.rView.top
- );
- viewRect = LIST.rView;
- InsetRect(&viewRect, -1, -1);
- InvalRect(&viewRect);
- MoveControl(
- LIST.vScroll,
- LIST.rView.right - kScrollBarOffset,
- LIST.rView.top - 1
- );
- MoveControl(
- HSCROLL,
- LIST.rView.left - 1,
- LIST.rView.bottom - kScrollBarOffset
- );
- }
- }
- }
-
- /*
- * SizeLog: this is the list rectangle size and does not include the
- * horizontal and vertical scrollbars.
- */
- void
- SizeLog(
- ListHandle logListHandle,
- short newWidth,
- short newHeight
- )
- {
- Rect viewRect;
- Point cellSize;
- short horizontalMax;
-
- if (logListHandle != NULL) {
- viewRect = LIST.rView;
- InsetRect(&viewRect, -1, -1);
- InvalRect(&viewRect);
- /*
- * We put the horizontal scrollbar back into the list record so that
- * the list manager kindly resizes it. Then we take it out again.
- */
- LIST.hScroll = HSCROLL;
- LSize(newWidth, newHeight, logListHandle);
- LIST.hScroll = NULL;
- cellSize = LIST.cellSize;
- cellSize.h = width(LIST.rView);
- LCellSize(cellSize, logListHandle);
- horizontalMax = kMaxHorizontalScroll - width((**HSCROLL).contrlRect);
- if (horizontalMax < 0)
- horizontalMax = 0;
- SetCtlMax(HSCROLL, horizontalMax);
- HiliteControl(
- HSCROLL,
- (horizontalMax == 0) ? kDisabledControl : kActiveControl
- );
- viewRect = LIST.rView;
- InsetRect(&viewRect, -1, -1);
- InvalRect(&viewRect);
- }
- }
-
- /*
- * DoClickInLog: call this when there is a mouse down in the log area (or in
- * one of the scrollbars.
- */
- Boolean
- DoClickInLog(
- ListHandle logListHandle,
- const EventRecord *eventRecord
- )
- {
- #define EVENT (*eventRecord)
-
- Point mousePt;
- Boolean result;
- Rect viewRect;
- short part;
- ControlHandle theControl;
-
- if (logListHandle == NULL)
- result = FALSE;
- else {
- mousePt = EVENT.where;
- GlobalToLocal(&mousePt);
- /*
- * Handle clicks in the horizontal scrollbar:Do not pass them through
- * LClick, as it does not do what we want and besides, the scrollbar
- * isn't there any more.
- */
- if (PtInRect(mousePt, &(**HSCROLL).contrlRect)) {
- part = FindControl(mousePt, (**HSCROLL).contrlOwner, &theControl);
- if (part >= 0 && theControl == HSCROLL) {
- if (part == inThumb) {
- if (TrackControl(theControl, mousePt, NULL))
- ScrollLogList(theControl);
- }
- else {
- TrackControl(theControl, mousePt, (ProcPtr) ScrollLogAction);
- }
- }
- }
- else {
- viewRect = LIST.rView;
- viewRect.right += kScrollBarOffset;
- if ((result = PtInRect(mousePt, &viewRect)))
- (void) LClick(mousePt, EVENT.modifiers, logListHandle);
- }
- }
- return (result);
- #undef EVENT
- }
-
- /*
- * Log errors.
- */
- void
- LogStatus(
- ListHandle logListHandle,
- OSErr theError,
- const StringPtr infoText
- )
- {
- Handle macErrorHdl;
- Str255 msg;
- Str15 errorValue;
-
- if (logListHandle != NULL && theError != noErr) {
- pstrcpy(msg, infoText);
- pstrcat(msg, "\p: ");
- NumToString(theError, errorValue);
- pstrcat(msg, errorValue);
- macErrorHdl = GetResource('Estr', theError);
- if (macErrorHdl != NULL) {
- pstrcat(msg, "\p ");
- pstrcat(msg, (StringPtr) *macErrorHdl);
- ReleaseResource(macErrorHdl);
- }
- DisplayLogString(logListHandle, msg);
- }
- }
-
- /*
- * DisplayLogString
- * Call this function to store a string in the list.
- */
- void
- DisplayLogString(
- ListHandle logListHandle,
- const StringPtr theString
- )
- {
-
- Cell theCell;
- short theRow;
- Boolean scrollAtBottom;
-
- if (logListHandle != NULL) {
- /*
- * If there are already logLines in the
- * list, delete the first row of the list.
- * Then, in any case, append this datum at the
- * bottom.
- *
- * The scroll bars are managed as follows:
- * scroll bar is at the bottom, the new datum
- * is selected and autoscrolled into view.
- * Otherwise, the current cell is unchanged.
- */
- theRow = LIST.dataBounds.bottom;
- scrollAtBottom =
- (GetCtlValue(LIST.vScroll) == GetCtlMax(LIST.vScroll));
- if (theRow >= LOGINFO.logLines)
- LDelRow(1, 0, logListHandle);
- theRow = LAddRow(1, LIST.dataBounds.bottom, logListHandle);
- if (MemError() != noErr)
- goto failure;
- theCell.h = 0;
- theCell.v = theRow;
- LSetCell((Ptr) &theString[1], theString[0], theCell, logListHandle);
- if (MemError() != noErr)
- goto failure;
- if (scrollAtBottom) {
- theCell.v = 0;
- if (LGetSelect(TRUE, &theCell, logListHandle))
- LSetSelect(FALSE, theCell, logListHandle);
- theCell.v = theRow;
- LSetSelect(TRUE, theCell, logListHandle);
- LDoDraw(TRUE, logListHandle);
- LAutoScroll(logListHandle);
- }
- LDraw(theCell, logListHandle);
- }
- failure:
- return;
- }
-
- /*
- * ScrollLogAction is called by the Toolbox while executing the TrackControl
- * routine. It has to take care of scrolling the log when the user clicks on the
- * up/down arrow or page parts of the scroll bar.
- */
- static pascal void
- ScrollLogAction(
- register ControlHandle theControl,
- short partcode
- )
- {
- short delta;
-
- delta = (width((**theControl).contrlRect) * 7) / 8;
- switch (partcode) {
- case inUpButton: delta = -CharWidth('M'); break;
- case inPageUp: delta = -(delta); break;
- case inDownButton: delta = CharWidth('M'); break;
- case inPageDown: /* All set */ break;
- default: return; /* Mouse exited control */
- }
- SetCtlValue(theControl, GetCtlValue(theControl) + delta);
- ScrollLogList(theControl);
- }
-
- /*
- * ScrollLogList scrolls the list rectangle in the proper direction and updates
- * the list's horizontal indentation to match.
- */
- static void
- ScrollLogList(
- ControlHandle theControl
- )
- {
- ListHandle logListHandle;
- short delta;
- RgnHandle clipRgn;
- RgnHandle updateRgn;
- Rect viewRect;
-
- logListHandle = (ListHandle) GetCRefCon(theControl);
- /*
- * LIST.indent.h is negative when the cell is scrolled left. Get the
- * amount it's currently scrolled (as a positive value) and set delta
- * to the amount that must be scrolled. Delta will be positive to
- * scroll right (which means that the scrollbar has moved left).
- */
- delta = kZeroIndent - LIST.indent.h - GetCtlValue(theControl);
- if (delta != 0) {
- /*
- * We need to scroll the list cells. Get a clip rectangle so the
- * scrolling is limited to the drawing area, scroll it, and update
- * the stuff that came into view.
- */
- viewRect = LIST.rView;
- clipRgn = NewRgn();
- updateRgn = NewRgn();
- GetClip(clipRgn);
- ClipRect(&viewRect);
- ScrollRect(&viewRect, delta, 0, updateRgn);
- LIST.indent.h += delta;
- LUpdate(updateRgn, logListHandle);
- SetClip(clipRgn);
- DisposeRgn(updateRgn);
- DisposeRgn(clipRgn);
- }
- }
-
- /*
- * Draw the string stored in the list. The only difference between this function
- * and a "normal" LDEF is that we don't visually indicate selection.
- */
- static pascal void
- LogListDefProc(
- short listMessage,
- Boolean listSelect,
- Rect *listRect,
- Cell listCell,
- short listDataOffset,
- short listDataLen,
- ListHandle logListHandle
- )
- {
- #pragma unused (listCell)
-
- Boolean cellLockState;
- RGBColor saveForeColor;
- RGBColor saveBackColor;
-
- /*
- * If the userHandle isn't setup, do nothing: this is an initialization
- * or a spurious command while we're disposing of the list.
- */
- if (LIST.userHandle != NULL) {
- TextFont(LOGINFO.fontNumber);
- TextSize(LOGINFO.fontSize);
- if (COLOR_LIST) {
- GetForeColor(&saveForeColor);
- GetBackColor(&saveBackColor);
- RGBForeColor(&LOGINFO.foreColor);
- RGBBackColor(&LOGINFO.backColor);
- }
- switch (listMessage) {
- case lInitMsg:
- break;
- case lDrawMsg:
- EraseRect(listRect);
- if (listDataLen > 0) {
- /*
- * We don't indent in the vertical direction: by default,
- * it contains the font ascent which is fine for DrawText
- */
- cellLockState = HGetState(LIST.cells);
- HLock(LIST.cells);
- MoveTo(
- listRect->left + LIST.indent.h,
- listRect->top + LIST.indent.v
- );
- DrawText((Ptr) (*LIST.cells), listDataOffset, listDataLen);
- HSetState(LIST.cells, cellLockState);
- }
- if (listSelect == FALSE)
- break;
- /* Continue to do hilite */
- case lHiliteMsg:
- #if 0 /* Hiliting is disabled */
- #ifdef THINK_C
- HiliteMode &= ~(1 << hiliteBit);
- #else /* MPW */
- *((char *) HiliteMode) &= ~(1 << hiliteBit); /* Inside Mac V-61 */
- #endif
- InvertRect(listRect);
- #endif
- break;
- }
- if (COLOR_LIST) {
- RGBForeColor(&saveForeColor);
- RGBBackColor(&saveBackColor);
- }
- }
- }
-
-